package cm.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: yxh
 * @Description: EasyExcel 工具类-自定义导出
 * @Date: Created in 2025/2/24 10:00
 * @Modified By:
 */

public class EasyExcelUtils {

    public static OutputStream downloadResponse(String fileName){
        OutputStream out = null;
        try {
            RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
            HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse();
            response.setContentType("application/octet-stream");
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Method", "*");
            response.addHeader("Access-Control-Allow-Headers", "Content-Type");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-Disposition", "attachment; filename*=" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
            response.addHeader("Access-Control-Max-Age", "3600");
            out  = response.getOutputStream();
        }catch (IOException e) {
//            throw new BusinessException("500", "下载错误");
        }
       return out;
    }
    public static void closeStream( OutputStream out ){
        try {
            if (out != null) {
                out.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 导出数据, 导出字段顺序/表头在注解ExcelProperty中定义
     *
     * @param fileName     文件名
     * @param sheetName    sheet名
     * @param clazz        数据类型
     * @param data         数据集合
     */
    public static void write(String fileName, String sheetName, Class<?> clazz, Collection<?> data) {
        OutputStream out = downloadResponse(fileName);
        EasyExcel.write(out, clazz)
                .registerWriteHandler(new ExportStyleUtil())
                .sheet(sheetName)
                .doWrite(data);
    }

    /**
     * 导出数据, 自定义 列和顺序/表头
     *
     * @param fileName  文件名
     * @param clazz     数据类型
     * @param data      数据集合
     * @param column    需要导出的列
     * @param head      自定义excel表头
     */
    public static void write(String fileName, Class<?> clazz, Collection<?> data, Collection<String> column, Collection<String> head) {
        OutputStream  out = downloadResponse(fileName);
        EasyExcel
                .write(out, clazz)
                .registerWriteHandler(new ExportStyleUtil())
                .sheet()
                .head(head(head))
                .includeColumnFieldNames(column)
                .doWrite(dataList(data, column));
        closeStream(out);
    }

    /**
     * 导出数据, 自定义导出列和顺序
     * excel表头需要从ExcelProperty中获取
     *
     * @param fileName     文件名
     * @param clazz        数据类型
     * @param data         数据集合
     * @param column       需要导出的列
     */
    public static void write(String fileName, Class<?> clazz, Collection<?> data, Collection<String> column) {
        OutputStream  out = downloadResponse(fileName);
        EasyExcel
                .write(out, clazz)
                .registerWriteHandler(new ExportStyleUtil())
                .sheet()
                .head(head(clazz, column))
                .includeColumnFieldNames(column)
                .doWrite(dataList(data, column));
        closeStream(out);
    }

    /**
     * 按照指定头列表设置Excel头
     *
     * @param headList Excel头信息
     * @return
     */
    private static List<List<String>> head(Collection<String> headList) {
        List<List<String>> list = new ArrayList<>();
        for (String value : headList) {
            List<String> head = new ArrayList<>();
            head.add(value);
            list.add(head);
        }
        return list;
    }

    /**
     * 将注解中的头按传入列的顺序返回
     *
     * @param clazz      指定类
     * @param columnList 传入的列顺序
     * @return
     */
    public static List<List<String>> head(Class<?> clazz, Collection<String> columnList) {
        List<List<String>> list = new ArrayList<>();

        for (String column : columnList) {
            try {
                Field field = clazz.getDeclaredField(column);
                String[] value = field.getAnnotation(ExcelProperty.class).value();
                List<String> collect = Arrays.stream(value).collect(Collectors.toList());
                list.add(collect);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
        return list;
    }

    /**
     * 设置表格信息
     *
     * @param dataList 查询出的数据
     * @param column   需要显示的字段
     * @return
     */
    private static List<List<Object>> dataList(Collection<?> dataList, Collection<String> column) {
        List<List<Object>> list = new ArrayList<>();
        for (Object person : dataList) {
            List<Object> data = new ArrayList<>();
            for (String fieldName : column) {
                // 通过反射根据需要显示的字段，获取对应的属性值
                data.add(getFieldValue(fieldName, person));
            }
            list.add(data);
        }
        return list;
    }


    /**
     * 根据传入的字段获取对应的get方法，如name,对应的getName方法
     *
     * @param fieldName 字段名
     * @param obj       对象
     * @return
     */
    private static Object getFieldValue(String fieldName, Object obj) {
        try {
            String firstUpper = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstUpper + fieldName.substring(1);
            Method method = obj.getClass().getMethod(getter);
            return method.invoke(obj);
        } catch (Exception e) {
            return null;
        }
    }

}